package api.util;

import api.model.ApiMigration;
import api.model.ApiMigrationRecord;
import api.model.ApiMigrationTable;
import m.common.model.Model;
import m.common.model.util.*;
import m.system.db.TransactionManager;
import m.system.exception.MException;
import m.system.util.ClassUtil;
import m.system.util.JSONMessage;
import m.system.util.SystemUtil;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class ApiMigrationSendProgress {
    private final String oid;
    private ApiMigration model;
    public ApiMigrationSendProgress(String modelOid) throws Exception {
        this.oid=modelOid;
        this.model=ModelQueryList.getModel(ApiMigration.class,oid,"*");
    }
    public void execute() throws Exception {
        ModelUpdate.init(ApiMigrationRecord.class)
            .set("status",ApiMigrationRecord.Status_No)
            .set("errorMessage",null)
            .execute(
                QueryCondition.and(
                    QueryCondition.in("status",ApiMigrationRecord.Status_Error,ApiMigrationRecord.Status_Send),
                    QueryCondition.eq("migration.oid",oid)
                )
            );
        List<ApiMigrationTable> tableList= ModelQueryList.getModelList(ApiMigrationTable.class,new String[]{"oid"},null, QueryCondition.eq("migration.oid",oid));
        for(ApiMigrationTable table : tableList) updateTableNum(table.getOid());
        updateNum();
        AtomicInteger num= new AtomicInteger();
        for(int i=0;i<model.getThreadNum();i++) {
            new Thread(() -> {
                try {
                    num.getAndIncrement();
                    runExecute();
                    num.getAndDecrement();
                    if(num.get()==0){
                        model = ModelQueryList.getModel(ApiMigration.class, oid, "*");
                        if(model.getStatus().equals(ApiMigration.Status_Migration)
                                &&model.getSurplusNum()==0) {
                            model.setStatus(ApiMigration.Status_Migration_Stop);
                            ModelUpdateUtil.updateModel(model,"status");
                        }
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    TransactionManager.closeConnection();
                }
            }).start();
            Thread.sleep(1000);
        }
    }
    private void runExecute() throws Exception {
        List<ApiMigrationTable> tableList= ModelQueryList.getModelList(ApiMigrationTable.class,new String[]{"*"},null,
            QueryCondition.and(
                QueryCondition.eq("migration.oid",oid),
                QueryCondition.not(QueryCondition.eq("surplusNum",0))
            )
        );
        if(!tableList.isEmpty()) {
            for (ApiMigrationTable table : tableList) {
                if (!sendRecordList(table)) return;
            }
            runExecute();
        }else{
            this.model = ModelQueryList.getModel(ApiMigration.class, oid, "*");
            if(model.getExeProgress()==1){
                model.setStatus(ApiMigration.Status_Done);
                ModelUpdateUtil.updateModel(model,"status");
            }
        }
    }
    private boolean sendRecordList(ApiMigrationTable table) throws Exception {
        if (!model.getStatus().equals(ApiMigration.Status_Migration)) return false;
        List<ApiMigrationRecord> recordList;
        synchronized (this) {
            recordList = ModelQueryList.getModelList(ApiMigrationRecord.class, new String[]{"*"},
                new QueryPage(0, 1000),
                QueryCondition.and(
                    QueryCondition.eq("table.oid", table.getOid()),
                    QueryCondition.eq("status", ApiMigrationRecord.Status_No)
                )
            );
            if(!recordList.isEmpty()){
                updateRecordStatus(recordList,ApiMigrationRecord.Status_Send,null);
            }
        }
        if(!recordList.isEmpty()){
            if(sendInter(table,recordList)) {
                return sendRecordList(table);
            }else{
                return false;
            }
        }
        return true;
    }
    private void updateRecordStatus(List<ApiMigrationRecord> recordList,String status,String errorMessage) throws MException {
        ModelUpdate.init(ApiMigrationRecord.class)
            .set("status",status)
            .set("errorMessage",errorMessage)
            .execute(QueryCondition.in("oid",recordList.stream().map(ApiMigrationRecord::getOid).toArray()));
    }
    private boolean sendInter(ApiMigrationTable table, List<ApiMigrationRecord> recordList) throws Exception {
        this.model = ModelQueryList.getModel(ApiMigration.class, oid, "*");
        if (model.getStatus().equals(ApiMigration.Status_Migration)) {
            List<? extends Model> modelList = ModelQueryList.getModelList(
                (Class<Model>) ClassUtil.getClass(table.getTableClass()),
                new String[]{"*"}, null,
                QueryCondition.in("oid", recordList.stream().map(ApiMigrationRecord::getModelOid).toArray())
            );
            JSONMessage result = new ApiMigrationInterUtil(model).sendModelList(table.getTableClass(), modelList);
            SystemUtil.println("---------同步结果：",result);
            if (result.isSuccess()) {
                updateRecordStatus(recordList, ApiMigrationRecord.Status_Yes, null);
            } else {
                updateRecordStatus(recordList, ApiMigrationRecord.Status_Error, result.getMessage());
            }
            updateTableNum(table.getOid());
            updateNum();
            return true;
        }else{
            return false;
        }
    }
    private void updateTableNum(String tableOid) throws MException {
        ModelUpdate.init(ApiMigrationTable.class)
            .set("sendNum", ApiMigrationRecord.class,"count(1)",QueryCondition.and(QueryCondition.eq("table.oid",tableOid),QueryCondition.eq("status",ApiMigrationRecord.Status_Send)))
            .set("successNum", ApiMigrationRecord.class,"count(1)",QueryCondition.and(QueryCondition.eq("table.oid",tableOid),QueryCondition.eq("status",ApiMigrationRecord.Status_Yes)))
            .set("errorNum", ApiMigrationRecord.class,"count(1)",QueryCondition.and(QueryCondition.eq("table.oid",tableOid),QueryCondition.eq("status",ApiMigrationRecord.Status_Error)))
            .setExp("surplusNum","#{recordNum}-#{sendNum}-#{successNum}-#{errorNum}")
            .setExp("exeProgress","round(#{successNum}/#{recordNum},6)")
            .execute(tableOid);
    }
    private void updateNum() throws MException {
        ModelUpdate.init(ApiMigration.class)
            .set("sendNum",ApiMigrationTable.class,"sum(#{sendNum})",QueryCondition.eq("migration.oid",oid))
            .set("successNum",ApiMigrationTable.class,"sum(#{successNum})",QueryCondition.eq("migration.oid",oid))
            .set("errorNum",ApiMigrationTable.class,"sum(#{errorNum})",QueryCondition.eq("migration.oid",oid))
            .setExp("surplusNum","#{recordNum}-#{sendNum}-#{successNum}-#{errorNum}")
            .setExp("exeProgress","round(#{successNum}/#{recordNum},6)")
            .execute(oid);
    }
}
